home *** CD-ROM | disk | FTP | other *** search
- #!/bin/sh
- #
- # USB-specific hotplug policy agent.
- #
- # This should handle 2.2.18+, 2.4.*, and 2.5.* USB hotplugging,
- # with a consistent framework for adding device and driver
- # specific handling.
- #
- # Normally, adding a usb device will modprobe a driver. If there
- # is a /etc/hotplug/usb/$DRIVER script set up, it will also run,
- # handling tasks like loading firmware or starting daemons.
- #
- # Kernel USB hotplug params include:
- #
- # ACTION=%s [add or remove]
- # DEVPATH=%s [in 2.5 kernels, /sys/$DEVPATH]
- # PRODUCT=%x/%x/%x
- # INTERFACE=%d/%d/%d [ for interface 0, if TYPE=0/*/* ]
- # TYPE=%d/%d/%d
- #
- # And if usbfs (originally called usbdevfs) is configured, also:
- #
- # DEVFS=/proc/bus/usb [gone in 2.5]
- # DEVICE=/proc/bus/usb/%03d/%03d
- #
- # This script then adds the variable:
- #
- # REMOVER=/var/run/usb/<some string unique to $DEVICE>
- #
- # This is the path where the script would like to find a remover, if
- # the target device needs one. This script is executed on remove if
- # it is executable when the remove happens.
- #
- # If usbfs is mounted on /proc/bus/usb, $DEVICE is a file which
- # can be read to get the device's current configuration descriptor.
- # (The "usbmodules" utility does that.) Or it can be used by a
- # user mode driver to interact with the usb device. USB hotplug
- # does *not* require usbfs (or sysfs) to work, although on 2.4
- # some devices work better if "usbmodules" can help.
- #
- # For Linux 2.5+ kernels, there's no need for "usbmodules". For
- # two reasons: first, hotplug is invoked for each interface, not
- # just the first one. Second, sysfs exposes descriptors so they
- # are easy to use for "coldplug" event simulation. (But sysfs is
- # not a replacement for the driver I/O capabilities in usbfs.)
- #
- # On systems using Linux 2.4.* kernels, be sure to use the right
- # modutils (2.4.2+). That ensures that hotplugging uses the list
- # of modules installed for your kernel, rather than the one that's
- # included here for use on systems without MODULE_DEVICE_TABLE
- # support.
- #
- #
- # HISTORY:
- #
- # 20-Nov-2002 some 2.5 updates; handle new 'device' hotplug; turn off
- # 'sleep' hack since hcds must all queue control traffic
- # 08-Aug-2002 support for multiple usermaps (maxk), minor cleanup
- # 18-Jan-2002 fix match algorithm in usb_map_modules()
- # 14-Jan-2002 fix work around 2.2 brokeness of $PRODUCT
- # 09-Jan-2002 REMOVER for system without usbdevfs
- #
- # 14-Mar-2001 Cleanup, bitmask the match_flags
- # 26-Feb-2001 Cleanup, support comments (Gioele Barabucci)
- # 15-Feb-2001 Remove use of "<<" (Adam Richter)
- # 23-Jan-2001 Update 2.2 handling; unfortunately there's no "feature
- # test" that can work robustly
- # 05-Jan-2001 Quick hack for kernel 2.4.0 and modutils 2.4.1
- # 03-Jan-2001 Initial version of "new" hotplug agent, using feedback
- # and contributions from Adam Richter, Ryan VanderBijl,
- # Norbert Preining, Florian Lohoff, David Brownell and
- # others. To replace the original /etc/usb/policy. (db)
- #
- # $Id: usb.agent,v 1.43 2004/09/20 22:50:11 kroah Exp $
- #
-
- if [ -f /etc/sysconfig/usb ]; then
- . /etc/sysconfig/usb
- fi
- if [ -f /etc/conf.d/usb ]; then
- . /etc/conf.d/usb
- fi
-
- cd /etc/hotplug
- . ./hotplug.functions
- # DEBUG=yes export DEBUG
-
- # generated by modutils, for current 2.4.x (and later) kernels
- MAP_CURRENT=$MODULE_DIR/modules.usbmap
-
- # used if MAP_CURRENT is missing; for 2.2.x kernels
- MAP_DISTMAP=$HOTPLUG_DIR/usb.distmap
-
- #
- # used for kernel drivers that don't show up in CURRENT or DISTMAP,
- # currently input drivers (joysticks, keyboards, etc). newer systems
- # should use input hotplug events instead.
- #
- MAP_HANDMAP=$HOTPLUG_DIR/usb.handmap
-
- #
- # used to run config scripts for user mode drivers (jPhoto, gPhoto2,
- # rio500 tools, etc) ... instead of naming kernel modules, it names
- # config scripts. those could change $DEVICE permissions, etc.
- #
- # for purely user mode drivers, scripts $HOTPLUG_DIR/usb/NAME should be
- # installed with usermap files in $HOTPLUG_DIR/usb/NAME.usermap instead
- # of continuing to use/modify $MAP_USERMAP
- #
- MAP_USERMAP=$HOTPLUG_DIR/usb.usermap
-
-
- # accumulates list of modules we may care about
- DRIVERS=""
-
- if [ "$ACTION" = "" ]; then
- mesg Bad USB agent invocation, no action
- exit 1
- fi
-
- # starting in kernel 2.5 there are two kinds of USB hotplug events.
- # - per-interface; 2.2/2.4 kernels only reported the first one.
- # "new" events have nonzero /sys/$DEVPATH/bInterfaceNumber
- # - per-device; "new" events don't have $PRODUCT
- SYSFS=/sys
- if [ "$PRODUCT" = "" ]; then
- # this is either an error, or we're on a 2.5 system...
- if [ "$DEVPATH" = "" ]; then
- mesg Bad USB agent invocation
- exit 1
- fi
-
- # sysfs files may already be gone
- if [ $ACTION = 'remove' ]; then
- exit 0
- fi
-
- # we could be running before usb populated these attributes...
- if [ ! -f $SYSFS/$DEVPATH/bNumConfigurations ]; then
- # FIXME wait till they appear, or N seconds elapse
- sleep 2
- fi
-
- # this could care about changing the default config, or warning
- # when the user hooked a fast device up so it runs slowly.
- if [ ! -f $SYSFS/$DEVPATH/bNumConfigurations ]; then
- exit 0
- fi
- TMP=$(cat $SYSFS/$DEVPATH/bNumConfigurations)
- if [ $TMP -ne 1 ] && [ "$ACTION" = add ]; then
- mesg Keeping default configuration with $SYSFS/$DEVPATH
- fi
-
- # NOTE: it might be good to add an extension hook here rather
- # than ignore these events, but even device-scope tasks such
- # as firmware download can still use the interface-0 event
- # (as they did with 2.2/2.4 hotplug setup scripts).
- exit 0
- fi
-
- # we can't "unset IFS" on bash1, so save a copy
- DEFAULT_IFS="$IFS"
-
- #
- # Each modules.usbmap format line corresponds to one entry in a
- # MODULE_DEVICE_TABLE(usb,...) declaration in a kernel file.
- #
- # Think of it as a database column with up to three "match specs"
- # to associate kernel modules with particular devices or classes
- # of device. The match specs provide a reasonably good filtering
- # mechanism, but some driver probe() routines need to provide
- # extra filtering.
- #
-
- usb_convert_vars ()
- {
- # work around 2.2.early brokenness
- # munges the usb_bcdDevice such that it is a integer rather
- # than a float: e.g. 1.0 become 0100
- PRODUCT=`echo $PRODUCT | sed -e "s+\.\([0-9]\)$+.\10+" -e "s/\.$/00/" \
- -e "s+/\([0-9]\)\.\([0-9][0-9]\)+/0\1\2+" \
- -e "s+/\([0-9][0-9]\)\.\([0-9][0-9]\)+/\1\2+"`
- set $(echo $PRODUCT | sed -e 's+\([^/]*\)/\([^/]*\)/\(.*\)+\1 \2 \3+')
- usb_idVendor=$((0x$1))
- usb_idProduct=$((0x$2))
- usb_bcdDevice=$((0x$3))
-
- if [ "$TYPE" != "" ]; then
- IFS=/
- set $TYPE ''
- usb_bDeviceClass=$1
- usb_bDeviceSubClass=$2
- usb_bDeviceProtocol=$3
- IFS="$DEFAULT_IFS"
- elif [ -r $SYSFS/$DEVPATH/bDeviceClass ]; then
- usb_bDeviceClass=$((0x$(cat $SYSFS/$DEVPATH/bDeviceClass)))
- usb_bDeviceSubClass=$((0x$(cat $SYSFS/$DEVPATH/bDeviceSubClass)))
- usb_bDeviceProtocol=$((0x$(cat $SYSFS/$DEVPATH/bDeviceProtocol)))
- else
- # out-of-range values
- usb_bDeviceClass=1000
- usb_bDeviceSubClass=1000
- usb_bDeviceProtocol=1000
- fi
-
- if [ "$INTERFACE" != "" ]; then
- IFS=/
- set $INTERFACE ''
- usb_bInterfaceClass=$1
- usb_bInterfaceSubClass=$2
- usb_bInterfaceProtocol=$3
- IFS="$DEFAULT_IFS"
- elif [ -r $SYSFS/$DEVPATH/bInterfaceClass ]; then
- usb_bInterfaceClass=$((0x$(cat $SYSFS/$DEVPATH/bInterfaceClass)))
- usb_bInterfaceSubClass=$((0x$(cat $SYSFS/$DEVPATH/bInterfaceSubClass)))
- usb_bInterfaceProtocol=$((0x$(cat $SYSFS/$DEVPATH/bInterfaceProtocol)))
- else
- # out-of-range values
- usb_bInterfaceClass=1000
- usb_bInterfaceSubClass=1000
- usb_bInterfaceProtocol=1000
- fi
- }
-
- USB_MATCH_VENDOR=$((0x0001))
- USB_MATCH_PRODUCT=$((0x0002))
- USB_MATCH_DEV_LO=$((0x0004))
- USB_MATCH_DEV_HI=$((0x0008))
- USB_MATCH_DEV_CLASS=$((0x0010))
- USB_MATCH_DEV_SUBCLASS=$((0x0020))
- USB_MATCH_DEV_PROTOCOL=$((0x0040))
- USB_MATCH_INT_CLASS=$((0x0080))
- USB_MATCH_INT_SUBCLASS=$((0x0100))
- USB_MATCH_INT_PROTOCOL=$((0x0200))
-
- #
- # stdin is "modules.usbmap" syntax
- # on return, all matching modules were added to $DRIVERS
- #
- usb_map_modules ()
- {
- # look at each usb_device_id entry
- # collect all matches in $DRIVERS
-
- while read line
- do
- # comments are lines that start with "#" ...
- # be careful, they still get parsed by bash!
- case "$line" in
- \#*) continue ;;
- "") continue ;;
- esac
-
- set $line
-
- module=$1
- match_flags=$(($2))
-
- idVendor=$(($3))
- idProduct=$(($4))
- bcdDevice_lo=$(($5))
- bcdDevice_hi=$(($6))
-
- bDeviceClass=$(($7))
- bDeviceSubClass=$(($8))
- bDeviceProtocol=$(($9))
-
- shift 9
- bInterfaceClass=$(($1))
- bInterfaceSubClass=$(($2))
- bInterfaceProtocol=$(($3))
-
- : checkmatch $module
-
- : idVendor $idVendor $usb_idVendor
- if [ $USB_MATCH_VENDOR -eq $(( $match_flags & $USB_MATCH_VENDOR )) ] &&
- [ $idVendor -ne $usb_idVendor ]; then
- continue
- fi
-
- : idProduct $idProduct $usb_idProduct
- if [ $USB_MATCH_PRODUCT -eq $(( $match_flags & $USB_MATCH_PRODUCT )) ] &&
- [ $idProduct -ne $usb_idProduct ]; then
- continue
- fi
-
- : bcdDevice range $bcdDevice_hi $bcdDevice_lo actual $usb_bcdDevice
- if [ $USB_MATCH_DEV_LO -eq $(( $match_flags & $USB_MATCH_DEV_LO )) ] &&
- [ $usb_bcdDevice -lt $bcdDevice_lo ]; then
- continue
- fi
-
- # bcdDevice_lo <= bcdDevice <= bcdDevice_hi
- if [ $USB_MATCH_DEV_HI -eq $(( $match_flags & $USB_MATCH_DEV_HI )) ] &&
- [ $usb_bcdDevice -gt $bcdDevice_hi ]; then
- continue
- fi
-
- : bDeviceClass $bDeviceClass $usb_bDeviceClass
- if [ $USB_MATCH_DEV_CLASS -eq $(( $match_flags & $USB_MATCH_DEV_CLASS )) ] &&
- [ $bDeviceClass -ne $usb_bDeviceClass ]; then
- continue
- fi
- : bDeviceSubClass $bDeviceSubClass $usb_bDeviceSubClass
- if [ $USB_MATCH_DEV_SUBCLASS -eq $(( $match_flags & $USB_MATCH_DEV_SUBCLASS )) ] &&
- [ $bDeviceSubClass -ne $usb_bDeviceSubClass ]; then
- continue
- fi
- : bDeviceProtocol $bDeviceProtocol $usb_bDeviceProtocol
- if [ $USB_MATCH_DEV_PROTOCOL -eq $(( $match_flags & $USB_MATCH_DEV_PROTOCOL )) ] &&
- [ $bDeviceProtocol -ne $usb_bDeviceProtocol ]; then
- continue
- fi
-
- # NOTE: for now, this only checks the first of perhaps
- # several interfaces for this device.
-
- : bInterfaceClass $bInterfaceClass $usb_bInterfaceClass
- if [ $USB_MATCH_INT_CLASS -eq $(( $match_flags & $USB_MATCH_INT_CLASS )) ] &&
- [ $bInterfaceClass -ne $usb_bInterfaceClass ]; then
- continue
- fi
- : bInterfaceSubClass $bInterfaceSubClass $usb_bInterfaceSubClass
- if [ $USB_MATCH_INT_SUBCLASS -eq $(( $match_flags & $USB_MATCH_INT_SUBCLASS )) ] &&
- [ $bInterfaceSubClass -ne $usb_bInterfaceSubClass ]; then
- continue
- fi
- : bInterfaceProtocol $bInterfaceProtocol $usb_bInterfaceProtocol
- if [ $USB_MATCH_INT_PROTOCOL -eq $(( $match_flags & $USB_MATCH_INT_PROTOCOL )) ] &&
- [ $bInterfaceProtocol -ne $usb_bInterfaceProtocol ]; then
- continue
- fi
-
- # It was a match!
- DRIVERS="$module $DRIVERS"
- : drivers $DRIVERS
- done
- }
-
- #
- # declare a REMOVER name that the add action can use to create a
- # remover, or that the remove action can use to execute a remover.
- #
- if [ "$DEVPATH" != "" ]; then
- # probably, 2.6.x
- REMOVER=/var/run/usb/$(readlink -f $SYSFS/$DEVPATH | sed -e 's;/;%;g')
- elif [ "$DEVICE" != "" ]; then
- # 2.4.x?
- REMOVER=/var/run/usb/$(echo $DEVICE | sed -e 's;/;%;g')
- else
- # should not happen?
- REMOVER=/var/run/usb/$(echo "$INTERFACE/$PRODUCT/$TYPE" | sed -e 's;/;%;g')
- fi
- export REMOVER
-
- #
- # What to do with this USB hotplug event?
- #
- case $ACTION in
-
- add)
- # partial workaround for 2.4 uhci/usb-uhci driver problem: they don't
- # queue control requests, so device drivers can confuse each other if
- # they happen to issue requests at the same time ... it happens easily
- # with slow HID devices and "usbmodules".
- # starting with 2.5 (DEVPATH set), all hcds must queue control traffic.
- if [ "$DEVPATH" = "" ]; then
- sleep 3
- fi
-
- usb_convert_vars
-
- FOUND=false
- if [ -f $SYSFS/$DEVPATH/manufacturer ]; then
- LABEL="USB `cat $SYSFS/$DEVPATH/manufacturer` `cat $SYSFS/$DEVPATH/product`"
- else
- LABEL="USB product $PRODUCT"
- fi
-
- if [ -e "$REMOVER" ]; then
- rm -f "$REMOVER"
- fi
-
- # on 2.4 systems, modutils 2.4.2+ maintains MAP_CURRENT
- # ... otherwise we can't rely on it (sigh)
- case "$KERNEL" in
- 2.4.*|2.5.*|2.6.*)
- if [ -r $MAP_CURRENT ]; then
- load_drivers usb $MAP_CURRENT "$LABEL"
- fi;;
- *)
- if [ -r $MAP_DISTMAP ]; then
- load_drivers usb $MAP_DISTMAP "$LABEL"
- fi;;
- esac
- if [ "$DRIVERS" != "" ]; then
- FOUND=true
- fi
-
- # cope with special driver module configurations
- # (mostly HID devices, until we have an input.agent)
- # not needed on 2.6 - they are loaded by hotplug
- case "$KERNEL" in
- 2.6.* )
- : nothing
- ;;
- * )
- if [ -r $MAP_HANDMAP ]; then
- load_drivers usb $MAP_HANDMAP "$LABEL"
- if [ "$DRIVERS" != "" ]; then
- FOUND=true
- fi
- fi
- ;;
- esac
-
- # some devices have user-mode drivers (no kernel module, but config)
- # or specialized user-mode setup helpers
- MODPROBE=:
- for MAP in $MAP_USERMAP $HOTPLUG_DIR/usb/*.usermap
- do
- if [ -r $MAP ]; then
- load_drivers usb $MAP "$LABEL"
- if [ "$DRIVERS" != "" ]; then
- FOUND=true
- fi
- fi
- done
-
- if [ "$FOUND" = "false" ]; then
- debug_mesg "... no modules for $LABEL"
- exit 2
- fi
- ;;
-
- remove)
- if [ -x $REMOVER ]; then
- $REMOVER
- fi
- rm -f $REMOVER
-
- if [ -x /usr/sbin/updfstab ]; then
- /usr/sbin/updfstab
- fi
- ;;
-
- *)
- debug_mesg USB $ACTION event not supported
- exit 1
- ;;
-
- esac
-